/**
	The Minerva Project, DAG Scheduling Tools
	Copyright (C) 2006 Florin Pop & Stefan Bucur

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 */
package ro.pub.cs.minerva.task;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.sun.org.apache.xerces.internal.impl.xpath.regex.ParseException;

/**
 * @author Florin Pop
 *
 */
public class TasksConfiguration {
	
	private String configurationFile;
	private HashMap<String, Task> taskList;
	
	public TasksConfiguration() {
		this.configurationFile = "minerva/tasks50.xml";
		this.taskList = new HashMap<String, Task>();
		this.getTasksConfiguration();
	}
	
	public TasksConfiguration(String configurationFile) {
		this.configurationFile = configurationFile;
		this.taskList = new HashMap<String, Task>();
		this.getTasksConfiguration();
	}
	
	/**
	 * @param configurationFile
	 * @param taskList
	 */
	public TasksConfiguration(String configurationFile, HashMap<String, Task> taskList) {
		this.configurationFile = configurationFile;
		this.taskList = taskList;
	}

	/**
	 * @return the configurationFile
	 */
	public String getConfigurationFile() {
		return configurationFile;
	}

	/**
	 * @param configurationFile the configurationFile to set
	 */
	public void setConfigurationFile(String configurationFile) {
		this.configurationFile = configurationFile;
	}

	/**
	 * @return the taskList
	 */
	public HashMap<String, Task> getTaskList() {
		return taskList;
	}

	/**
	 * @param taskList the taskList to set
	 */
	public void setTaskList(HashMap<String, Task> taskList) {
		this.taskList = taskList;
	}
	
	private Document parseXMLFile() {
		
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setIgnoringElementContentWhitespace(true);
		
		DocumentBuilder db;
		Document document = null;
		
		// try to create a new document builder
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException pce) {
			System.err.println("Wrong parser configuration: " + pce.getMessage());
			return null;
		}
		
		File xmlFile = new File(configurationFile);
		
		// try to parse xml file
		try {
			document = db.parse(xmlFile);
		} catch (SAXException saxe) {
			System.err.println("Wrong XML file structure: " + saxe.getMessage());
			return null;
		}
		catch (IOException ioe) {
		    System.out.println("Could not read source file: " + ioe.getMessage());
		}
		
		return document;
	}
	
	private void getTasksConfiguration() {
		// parse XML file -> XML document will be build
		Document document = parseXMLFile();
		
		// get root node of xml tree structure
		Node root = document.getDocumentElement();
		if (root == null) {
        	throw new RuntimeException("Error at parsing XML document...");
        }
        
		this.taskList = getTasksFromDocument(root);
	}
	
	private HashMap<String, Task> getTasksFromDocument(Node root) {
		
		HashMap<String, Task> taskList = new HashMap<String, Task>();
		
		int userId=0;
		
		String rootName = root.getNodeName();
		if(!rootName.equals("Tasks")) {
			return taskList; // empty list
		} else {
			int i = 1;
			try {
				NodeList nodeList = root.getChildNodes();
				Node parent = nodeList.item(i);
				if (parent.getNodeType() == Node.ELEMENT_NODE && parent.getNodeName().equals("userId")) {
					userId = new Integer(getElementValue(parent)).intValue();
				}
				
				for (i = i + 2; i < nodeList.getLength(); i+=2) {
			
					Task task = new Task();
					task.setUserId(userId);
					String arrivingDate="", arrivingTime="";
					
					parent = nodeList.item(i);
					if ( parent.getNodeType() == Node.ELEMENT_NODE && parent.getNodeName().equals("task")) {
						
						NodeList children = parent.getChildNodes();
						Node child;
						
						for (int j = 1; j < children.getLength(); j+=2) {
							child = children.item(j);
							if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("taskId")) {
								task.setTaskId(getElementValue(child));
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("path")) {
								task.setPath(getElementValue(child));
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("arrivingDate")) {
								arrivingDate = getElementValue(child);
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("arrivingTime")) {
								arrivingTime = getElementValue(child);
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("arguments")) {
								task.setArguments(getElementValue(child));
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("input")) {
								task.setInput(getElementValue(child));
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("output")) {
								task.setOutput(getElementValue(child));
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("error")) {
								task.setError(getElementValue(child));
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("requirements")) {
								TaskRequirements taskRequirements = new TaskRequirements();
								
								NodeList nephews = child.getChildNodes();
								Node nephew;
								
								for (int k = 1; k < nephews.getLength(); k+=2) {
									nephew = nephews.item(k);
									if (nephew.getNodeType() == Node.ELEMENT_NODE && nephew.getNodeName().equals("memory")) {
										String ev = getElementValue(nephew);
										double memory = 0D;
										int index;
										if ((index = ev.indexOf("MB")) != -1 && ev.endsWith("MB")) {
											memory = (new Double(ev.substring(0,index))).doubleValue();
										}
										taskRequirements.setMemory(memory);
									} else if (nephew.getNodeType() == Node.ELEMENT_NODE && nephew.getNodeName().equals("cpuPower")) {
										String ev = getElementValue(nephew);
										double cpuPower = 0D;
										int index;
										if ((index = ev.indexOf("MHZ")) != -1 && ev.endsWith("MHZ")) {
											cpuPower = (new Double(ev.substring(0,index))).doubleValue();
										}
										taskRequirements.setCpuPower(cpuPower);
									} else if (nephew.getNodeType() == Node.ELEMENT_NODE && nephew.getNodeName().equals("processingTime")) {
										taskRequirements.setProcessingTime(new Long(getElementValue(nephew)).longValue());
									} else if (nephew.getNodeType() == Node.ELEMENT_NODE && nephew.getNodeName().equals("deadlineTime")) {
										Date d = new Date();
										SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
										try {
											d = sdf.parse(getElementValue(nephew));
											taskRequirements.setDeadlineTime(d.getTime());
										}
										catch(ParseException pe) {
											pe.printStackTrace();
										}
									} else if (nephew.getNodeType() == Node.ELEMENT_NODE && nephew.getNodeName().equals("schedulePriority")) {
										taskRequirements.setSchedulePriority(new Integer(getElementValue(nephew)).intValue());
									}																			
								}
								task.setTaskReqs(taskRequirements);
							} else if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("nrexec")) {
								task.setNrexec(new Integer(getElementValue(child)).intValue());
							}
						}
						
						Date d = new Date();
						SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
						try {
							d = sdf.parse(arrivingDate+" "+arrivingTime);
							task.setArrivingTime(d.getTime());
						}
						catch(ParseException pe) {
							pe.printStackTrace();
						}
					}
					taskList.put(task.getTaskId(), task);
				}
			} catch (Exception e) {
				System.err.println("Error at parsing XML document..." + e.getMessage());
				return null;
			}
		}
		return taskList;
	}
	  
	/**
	 * method which returns element value from an XML file;
	 * receives as a parameter the element from the XML file
	 * @param elem 
	 */
	private final static String getElementValue( Node elem ) {
		Node kid;
		if( elem != null ){
			if (elem.hasChildNodes()){
				for( kid = elem.getFirstChild(); kid != null; kid = kid.getNextSibling() ) {
					if( kid.getNodeType() == Node.TEXT_NODE  ){
						return kid.getNodeValue();
                    }
                }
            }
        }
        return "";
    }
	
	public Task getTask(String taskId) {
		return this.taskList.get(taskId);
	}
}
